using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp.Receive
{
    /// <summary>
    ///     This class represents a work item used to receive data from client and send back to initiator on separate connection.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public class DuplexWorkItem : IWorkItem
    {
        #region Private Instance Members

        private WorkItemStatusTypes _status;            // The status of the work item
        private ReceiveLocation     _receiveLocation;   // A reference to the receive location
        private Socket              _clientSocket;      // The client socket that has data
        private Socket              _initiatorSocket;   // The original initiating client that the data will be sent to

        #endregion

        #region Constructor

        /// <summary>
        ///     This constructor initializes the class.
        /// </summary>
        public DuplexWorkItem(ReceiveLocation receiveLocation, Socket clientSocket, Socket initiatorSocket)
        {
            // Validate arguments
            if (receiveLocation == null) throw new ArgumentNullException("receiveLocation");
            if (clientSocket == null) throw new ArgumentNullException("clientSocket");
            if (initiatorSocket == null) throw new ArgumentNullException("initiatorSocket");

            // Initialize members
            this._receiveLocation = receiveLocation;
            this._clientSocket = clientSocket;
            this._initiatorSocket = initiatorSocket;
            this._status = WorkItemStatusTypes.Ready;
        }

        #endregion

        #region Private Instance Methods

        /// <summary>
        ///     This method streams the data off the socket onto disk in binary format.
        /// </summary>
        private void SaveDataOnSocket()
        {
            // Try and stream data off socket onto disk
            using (NetworkStream stream = new NetworkStream(this.ClientSocket, true))
            {
                DeadMessage.WriteStreamToDisk(this.ReceiveLocation.Adapter, stream);
                stream.Close();
            }
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        ///     This property returns the receive location that produced this work item.
        /// </summary>
        public ReceiveLocation ReceiveLocation
        {
            get { return (this._receiveLocation); }
        }

        /// <summary>
        ///     This property returns the client socket that has data.
        /// </summary>
        public Socket ClientSocket
        {
            get { return (this._clientSocket); }
        }

        /// <summary>
        ///     This property returns the initiator socket that will receive the data.
        /// </summary>
        public Socket InitiatorSocket
        {
            get { return (this._initiatorSocket); }
        }

        #endregion

        #region IWorkItem Members

        #region Public Instance Properties

        /// <summary>
        ///     This property gets or sets the status of the work item.
        /// </summary>
        public WorkItemStatusTypes Status
        {
            get { return (this._status); }
            set { this._status = value; }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        ///     This method saves the content of the work item to the folder location specified by the argument.
        /// </summary>
        public void Save()
        {
            // Check state of work item
            switch (this.Status)
            {
                case WorkItemStatusTypes.Ready:
                case WorkItemStatusTypes.InProgress:

                    // Determine if we can stream data off socket
                    if (this.ClientSocket != null)
                    {
                        if ((this.ClientSocket.Connected) && (this.ClientSocket.Available > 0))
                        {
                            this.SaveDataOnSocket();
                        }
                    }
                    break;

                case WorkItemStatusTypes.Complete:

                    // Nothing to do
                    break;
            }
        }

        #endregion

        #endregion
    }
}
